/**
 * @copyright Copyright (c) 2021 Oray Inc. All rights reserverd.
 * No Part of this file may be reproduced, stored
 * in a retrieval system, or transmitted, in any form, or by any means,
 * electronic, mechanical, photocopying, recording, or otherwise,
 * without the prior consent of Oray Inc.
 * 
 * @file template_queue.hpp
 * @author sunchao (sunchao@oray.com)
 * @brief 模板队列,支持阻塞和非阻塞两种方式
 * @version 0.1
 * @date 2021-09-24
 */
#ifndef _TEMPLATE_QUEUE_H
#define _TEMPLATE_QUEUE_H
#include <condition_variable>
#include <list>
#include <assert.h>

/**
 * @brief 模板队列
 *      \li 建议使用T使用指针
 *      \li T为非指针的实体时需要实现对应的赋值和拷贝函数   
 *          T(const T& task);
 *          T& operator = (const T& task);
 * 
 */
template<typename T>
class template_queue
{
public:
    template_queue() : mutex_(), condvar_(), queue_()
    {

    }

    /**
     * @brief 将数据插入queue尾部
     *      
     * 
     * @param task T
     */
    void push(const T& task)
    {
        std::lock_guard<std::mutex> lock(mutex_);
        queue_.push_back(task);
        condvar_.notify_all();
    }

    /**
     * @brief 将数据插入queue头部
     *      \li 多线程情况下不一定能够按照原始顺序被取出
     * 
     * @param task T
     */
    void repush(const T& task)
    {
        std::lock_guard<std::mutex> lock(mutex_);
        queue_.push_front(task);
        condvar_.notify_all();
    }

    /**
     * @brief 阻塞式获取queue中数据
     *      
     * @return T
     */
    T pop_block()
    {
        std::unique_lock<std::mutex> lock(mutex_);
        condvar_.wait(lock, [this]{return !queue_.empty(); });
        assert(!queue_.empty());
        T front(queue_.front());
        queue_.pop_front();

        return front;
    }

    /**
     * @brief 非阻塞式获取queue中数据
     * 
     * @param task 接收的T
     * @return bool queue中是否有值
     */
    bool pop_noblock(T& task, unsigned int milliseconds)
    {

        {
            std::lock_guard<std::mutex> lock(mutex_);
            if(!queue_.empty())
            {
                assert(!queue_.empty());
                task = queue_.front();
                queue_.pop_front();

                return true;
            }
        }

        // std::lock_guard<std::mutex> lock(mutex_);
        std::unique_lock<std::mutex> lock(mutex_);
        if (condvar_.wait_for(lock, std::chrono::milliseconds(milliseconds)) == std::cv_status::timeout) 
        {
            return false;
        }   

        if(!queue_.empty())
        {
            assert(!queue_.empty());
            task = queue_.front();
            queue_.pop_front();

            return true;
        }
        
        return false;
    }

    /**
     * @brief 获取queue中数据个数
     * 
     * @return size_t queue中数据个数
     */
    size_t size() const
    {
        std::lock_guard<std::mutex> lock(mutex_);
        return queue_.size();
    }

private:
    template_queue(const template_queue& rhs);
    template_queue& operator = (const template_queue& rhs);

private:
    mutable std::mutex mutex_;
    std::condition_variable condvar_;
    std::list<T> queue_;

};

#endif //_TEMPLATE_QUEUE_H
